#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
    Objetos encargados de realizar operaciones entre dos parametros;
        uno denominado izquierdo y el otro derecho.    
"""

class EvaluadorAbstracto (object):
    """
        Define la interfaz necesaria para todo evaluador.        
    """
    def get_nombre(self):
        """
            Obtiene el nombre del evaluador, generalmente el simbolo.
        """
        raise NotImplementedError

    def __call__(self, izq, der):
        """
            Devolver el resultado de la operacion: izq <evaluador> der.
        """
        raise NotImplementedError

class Conjuncion(EvaluadorAbstracto):
    """
        Realiza la operacion AND, entre dos valores convertibles a bool        
    """
    def get_nombre(self):
        return '&'
    def __call__(self, izq, der):
        return (bool(izq) and bool(der))


class Disjuncion(EvaluadorAbstracto):
    """
        Realiza la operacion OR, entre dos valores convertibles a bool        
    """
    def get_nombre(self):
        return '|'
    def __call__(self, izq, der):
        return (bool(izq) or bool(der))


class MenorFloat(EvaluadorAbstracto):
    """
        Compara dos valores convertibles a float por menor; izq < der
    """
    def get_nombre(self):
        return '<'
    def __call__(self, izq, der):
        return float(izq) < float(der)


class MayorFloat(EvaluadorAbstracto):
    """
        Compara dos valores convertibles a float por mayor; izq > der
    """
    def get_nombre(self):
        return '>'
    def __call__(self, izq, der):
        return float(izq) > float(der)


class IgualFloat(EvaluadorAbstracto):
    """
        Compara dos valores convertibles a float por igual; izq = der
    """
    def get_nombre(self):
        return '='
    def __call__(self, izq, der):
        return float(izq) == float(der)


class DistintoFloat(EvaluadorAbstracto):
    """
        Compara dos valores convertibles a float por distinto; izq != der
    """
    def get_nombre(self):
        return '!='
    def __call__(self, izq, der):
        return float(izq) != float(der)


class SumaFloat(EvaluadorAbstracto):
    """
        Suma dos valores convertibles a float; izq + der
    """
    def get_nombre(self):
        return '+'
    def __call__(self, izq, der):
        return float(izq) + float(der)


class RestaFloat(EvaluadorAbstracto):
    """
        Resta dos valores convertibles a float; izq - der
    """
    def get_nombre(self):
        return '-'
    def __call__(self, izq, der):
        return float(izq) - float(der)


class MultiplicacionFloat(EvaluadorAbstracto):
    """
        Multiplica dos valores convertibles a float; izq * der
    """
    def get_nombre(self):
        return '*'
    def __call__(self, izq, der):
        return float(izq) * float(der)


class DivisionFloat(EvaluadorAbstracto):
    """
        Divide dos valores convertibles a float; izq / der
    """
    def get_nombre(self):
        return '/'
    def __call__(self, izq, der):
        return float(izq) / float(der)


class EvaluadorGenerico (EvaluadorAbstracto):
    """
        Evaluador preparado para comparar o operar con dos parametros y los
            siguientes operadores:
                parametros bool: \|, &
                parametros float: +, -, \*, /
                cualquier parametro: =, !=, <, >
    """
    def __init__(self, operacion):
        EvaluadorAbstracto.__init__(self)
        self._op = operacion

    def get_nombre(self):
        return self._op

    def __call__(self, izq, der):
        valor = None

        if not isinstance(izq, bool) and not isinstance(der, bool):
            try:
                float(izq)
                float(der)
                # Si las conversiones son posibles, modifico y las utilizo.
                izq = float(izq)
                der = float(der)
            except ValueError:
                # Si no sigo trabajando con el tipo de dato original.
                pass

        if self.get_nombre() == '=':
            valor = izq == der
        elif self.get_nombre() == '!=':
            valor = izq != der
        elif self.get_nombre() == '<':
            valor = izq < der
        elif self.get_nombre() == '>':
            valor = izq > der
        elif self.get_nombre() == '&':
            valor = bool(izq) & bool(der)
        elif self.get_nombre() == '|':
            valor = bool(izq) | bool(der)
        elif self.get_nombre() == '+':
            valor = float(izq) + float(der)
        elif self.get_nombre() == '-':
            valor = float(izq) - float(der)
        elif self.get_nombre() == '*':
            valor = float(izq) * float(der)
        elif self.get_nombre() == '/':
            valor = float(izq) / float(der)

        return valor


class _Factory (object):
    """
        Aplica el patron Factory Method para la construccion de los operadores
            en base a su simbolo.

        Los evaluadores generalmente deberian ser objetos sin estado.
    """        
    def __init__(self):
        # Este diccionario almacena los operadores a devolver.
        self._evaluadores = dict()

        # Operadores object => bool
        self._evaluadores['='] = EvaluadorGenerico('=')
        self._evaluadores['!='] = EvaluadorGenerico('!=')

        # Operadores float => bool
        self._evaluadores['<'] = MenorFloat()
        self._evaluadores['>'] = MayorFloat()

        # Operadores bool => bool
        self._evaluadores['&'] = Conjuncion()
        self._evaluadores['|'] = Disjuncion()

        # Operadores float => float
        self._evaluadores['+'] = SumaFloat()
        self._evaluadores['-'] = RestaFloat()
        self._evaluadores['*'] = MultiplicacionFloat()
        self._evaluadores['/'] = DivisionFloat()

    def create(self, operador):
        """
            Si el operador no esta definido lanza la excepcion KeyError.        
        """
        return self._evaluadores[operador]

    def get_definidos(self):
        return self._evaluadores.keys()

    def __call__(self, operador):
        """
            Alias de create
        """
        return self.create(operador)

factory = _Factory()

